// 源于Inferno utils/6l/l.h及相关文件。
// https:
// 
// 版权所有©1994-1999朗讯科技有限公司。保留所有权利。
// 版权所有©1995-1997 C H Forsyth(forsyth@terzarima.net)
// 部分版权©1997-1999维塔诺瓦有限公司
// 部分版权©2000-2007维塔诺瓦控股有限公司（www.vitanuova.com）
// 部分版权©20042006布鲁斯·埃利斯
// 修订版权©2000-2007朗讯科技有限公司及其他
// 部分版权©2009 Go作者。保留所有权利。
// 
// 特此免费授予获得本软件及相关文档文件（以下简称“软件”）副本
// 的任何人在本软件中不受限制地处理
// 的权利，包括但不限于
// 使用、复制、修改、合并、发布、分发、再许可的权利，和/或出售本软件的
// 副本，并允许提供本软件的人员在以下条件下这样做：
// 
// 上述版权声明和本许可声明应包含在
// 本软件的所有副本或大部分中。
// 
// 本软件按“原样”提供，不提供任何形式的明示或暗示担保，包括但不限于适销性担保、
// 适用于特定用途和非侵权性担保。在任何情况下，
// 作者或版权持有人均不对任何索赔、损害赔偿或其他
// 责任承担责任，无论是合同诉讼、侵权诉讼还是其他诉讼，均由
// 因本软件或本软件的使用或其他交易引起，或与本软件或本软件的使用或其他交易有关。

package obj

import (
	"bufio"
	"cmd/internal/dwarf"
	"cmd/internal/goobj"
	"cmd/internal/objabi"
	"cmd/internal/src"
	"cmd/internal/sys"
	"fmt"
	"sync"
	"sync/atomic"
)

// 地址是指令的参数。
// 一般形式及其编码为：
// 
// sym±偏移量（符号种类）（reg）（索引*刻度）
// 地址处的内存引用&sym（符号种类）+偏移量+reg+索引*刻度。
// 可以省略sym（SYMCIND），±offset，（reg），（索引*刻度）和*刻度中的任何一个。
// 如果（reg）和*scale都被省略，那么结果表达式（index）将被解析为（reg）。
// 要强制解析为索引*scale，请写入（索引*1）。
// 编码：
// type=type_MEM 
// name=symkind（name_AUTO，…）或0（姓名无）
// sym=sym 
// offset=±offset 
// reg=reg（reg_*）
// index=index（reg_*）
// scale=scale（1,2,4,8）
// 
// $<mem 
// /内存引用的有效地址<mem。
// 编码：与内存引用相同，但type=type_ADDR。
// 
// /$<±整数值>
// 这是$<mem>的一个特例，其中只存在±偏移量。
// 它有一个单独的类型，便于识别。
// 编码：
// type=type_CONST 
// 偏移量=±整数值
// 
// /*<mem>
// 通过内存引用的间接引用<mem>，如上定义。
// 仅在x86上用于调用/JMP*sym（SB），该函数调用/跳转到存储在数据字sym（SB）中的函数
// 指针，而不是名为sym（SB）的函数。
// 编码：同上，但type=type_INDIR。
// 
// $*$$<mem>
// 不再使用。
// 在具有实际SB寄存器的计算机上，$*$<mem>强制
// 指令编码使用完整的32位常量，而不是相对于SB的
// 引用。
// 
// $<浮点文字>
// /浮点常量值。
// Encoding:
// type=type_FCONST 
// val=浮点值
// 
// $<string literal，最多8个字符>
// string literal value（用于数据指令的原始字节）。
// Encoding:
// type=type_SCONST 
// val=string 
// 
// <register name>
// /任何寄存器：整数、浮点、控件、段等。
// 如果要查找特定的寄存器类型，必须检查类型和寄存器值范围。
// 编码：
// 类型=type_REG 
// REG=REG（REG_*）
// 
// x（PC）
// 编码：
// 类型=type_分支
// val=Prog*reference或其他偏移量=target PC（分支优先）
// 在这种形式中，x和y仅为整数文本，而不是任意表达式。
// 这避免了由于使用-作为分隔符而导致的解析歧义。
// ±是可选的。
// 如果文本的最后一个参数省略了-±y，编码仍应使用类型_TEXTSIZE（而不是类型_CONST），u.argsize=ArgsSizeUnknown。
// 编码：
// type=type_TEXTSIZE 
// offset=x 
// val=int32（y）
// 
// reg</shift，reg>>shift，reg->shift，reg@>shift 
// 移位寄存器值，用于ARM64。
// 在这种形式中，reg必须是寄存器，shift可以是寄存器或整数常量。
// 编码：
// 类型=类型_移位
// 手臂上：
// 偏移量=（reg&15）|移位类型<<5 |计数
// 移位类型=0，1，2，3表示<，->，@>
// 计数=（reg&15）<8 | 1<<4表示寄存器移位计数，（n&31）表示整数常量。
// 在ARM64上：
// 偏移量=（reg&31）<<16 |移位类型<<22 |（count&63）<<10 
// 移位类型=0，1，2表示<<，>>，->
// 
// （reg，reg）
// 目标寄存器对。当用作指令的最后一个参数时，
// 此形式表明两个寄存器都是目的地。
// 编码：
// type=type_regeg 
// reg=第一寄存器
// offset=第二寄存器
// 
// [reg，reg，reg reg reg]
// ARM 64，386/AMD64的寄存器列表。
// 编码：
// type=type_REGLIST 
// 在ARM上：
// 偏移量=列表中寄存器的位掩码；R0是低位。
// 在ARM64上：
// 偏移量=寄存器计数（Q:大小）|排列（操作码）|第一个寄存器
// 在386/AMD64上：
// reg=范围低寄存器
// 偏移量=2个压缩寄存器+种类标记（参见x86.encodeRegistrange）
// 
// reg，reg 
// ARM的寄存器对。
// TYPE_REGREG2 
// 
// （reg+reg）
// PPC64的寄存器对。
// 编码：
// type=type_MEM 
// reg=first register 
// index=second register 
// scale=1 
// 
// reg。[US]XT[BHWX]
// ARM64 
// 编码：
// type=type_REG 
// REG=REG_UX[BHWX]+寄存器+移位量
// 偏移量=（（REG&31）<16）|（exttype<13）|（amount<10）
// 
// ARM64 SIMD寄存器的寄存器安排
// 例如：V1。S4，V2。S2，V7。D2，V2。H4，V6。B16 
// 编码：
// type=type_REG 
// REG=REG_ARNG+寄存器+排列
// 
// REG<T> [index]
// ARM64的寄存器元素
// 编码：
// type=type_REG 
// REG=REG_ELEM+寄存器+排列
// index=element index 

type Addr struct {
	Reg    int16
	Index  int16
	Scale  int16 // 有时持有寄存器。
	Type   AddrType
	Name   AddrName
	Class  int8
	Offset int64
	Sym    *LSym

	// 参数值：
	// 对于类型_SCONST，字符串
	// 对于类型_FCONST，浮点64 
	// 对于类型_分支，a*Prog（可选）
	// 对于类型_TEXTSIZE，int32（可选）
	Val interface{}
}

type AddrName int8

const (
	NAME_NONE AddrName = iota
	NAME_EXTERN
	NAME_STATIC
	NAME_AUTO
	NAME_PARAM
	// 对name@GOT（SB）是对全局偏移量
	// 表中“name”项的引用。
	NAME_GOTREF
	// 表示这是对TOC锚的引用。
	NAME_TOCREF
)

// go:generate stringer-type addr类型

type AddrType uint8

const (
	TYPE_NONE AddrType = iota
	TYPE_BRANCH
	TYPE_TEXTSIZE
	TYPE_MEM
	TYPE_CONST
	TYPE_FCONST
	TYPE_SCONST
	TYPE_REG
	TYPE_ADDR
	TYPE_SHIFT
	TYPE_REGREG
	TYPE_REGREG2
	TYPE_INDIR
	TYPE_REGLIST
)

func (a *Addr) Target() *Prog {
	if a.Type == TYPE_BRANCH && a.Val != nil {
		return a.Val.(*Prog)
	}
	return nil
}
func (a *Addr) SetTarget(t *Prog) {
	if a.Type != TYPE_BRANCH {
		panic("setting branch target when type is not TYPE_BRANCH")
	}
	a.Val = t
}

func (a *Addr) SetConst(v int64) {
	a.Sym = nil
	a.Type = TYPE_CONST
	a.Offset = v
}

// Prog描述一条机器指令。
// 
// 通用指令格式为：
// 
// （1）As。从[，…重新启动]到
// /（2）As。Scond From，Reg[，…RestArgs]，To，RegTo2 
// 
// 其中As是操作码，其他是参数：
// From，Reg是源，To，RegTo2是目的地。wen jian defg
// 通常，并非所有的参数都存在。
// 例如，MOVL R1、R2仅使用As=MOVL、From=R1、To=R2进行编码。
// Scond字段为具有通用条件执行的系统（如arm）
// 保存附加条件位。
// （2）表单的存在是为了与旧代码兼容，
// ，以避免在一次转换中进行太多更改。
// /（1）格式足以表示任何类型的操作数组合。
// 
// 跳转指令使用To。Val字段指向目标*Prog，
// 它必须与跳转指令位于同一个链表中。
// 
// 给定函数的程序排列在通过链接字段链接的列表中。
// 
// 每个程序都在调试信息中的特定源代码行中收费，
// 由Pos.line（）指定。
// 每个程序都有一个定义其上下文的Ctxt字段。
// 出于性能原因，程序通常是大容量分配、缓存和重用的；
// 应该始终使用这些大容量分配器，而不是新的（Prog）。
// 
// 尚未提及的其他字段供后端使用，并且应该由程序列表的创建者将其保留为零。
type Prog struct {
	Ctxt     *Link     // 链接器上下文
	Link     *Prog     // 链接列表中的下一个程序
	From     Addr      // 第一个源操作数
	RestArgs []AddrPos // 可以打包任何不适合{Prog.From，Prog.To}
	To       Addr      // 目标操作数（第二个是下面的RegTo2）
	Pool     *Prog     // 常量池条目，对于arm，arm64后端
	Forwd    *Prog     // 对于x86后端
	Rel      *Prog     // 对于x86，arm后端
	Pc       int64     // 对于后端或汇编器：虚拟或实际程序计数器，取决于阶段
	Pos      src.XPos  // 此指令的源位置
	Spadj    int32     // 指令对堆栈指针的影响（增量或减量）
	As       As        // 汇编程序操作码
	Reg      int16     // 第二源操作数
	RegTo2   int16     // 第二目标操作数
	Mark     uint16    // arch特定项的位掩码
	Optab    uint16    // arch特定操作码索引
	Scond    uint8     // 描述指令后缀的位（例如ARM条件）
	Back     uint8     // 对于x86后端：向后分支状态
	Ft       uint8     // 对于x86后端：程序的类型索引。来自
	Tt       uint8     // 对于x86后端：Prog的类型索引。To 
	Isize    uint8     // 对于x86后端：以字节为单位的指令大小
}

// Pos指示oprand是源还是目标。
type AddrPos struct {
	Addr
	Pos OperandPos
}

type OperandPos int8

const (
	Source OperandPos = iota
	Destination
)

// From3Type返回p.GetFrom3（）。当
// p.GetFrom3（）返回nil时，键入或键入_NONE。
// 
// 已弃用：原因与Prog相同。从3开始。
func (p *Prog) From3Type() AddrType {
	if p.RestArgs == nil {
		return TYPE_NONE
	}
	return p.RestArgs[0].Type
}

// GetFrom3返回第二个源操作数（第一个是Prog.From）。
// 与Prog结合使用。来自和Prog。它使普通3操作数
// 大小写更易于使用。
// 
// 仅当RestArgs设置为SetFrom3时才应使用。
// 
// 不推荐：最好直接使用RestArgs或定义后端特定的getter。
// 用于简化到[]Addr的转换。
// 由于其脆弱性和缺乏保障，不鼓励使用。
func (p *Prog) GetFrom3() *Addr {
	if p.RestArgs == nil {
		return nil
	}
	return &p.RestArgs[0].Addr
}

// SetFrom3将[]Args{{a，0}赋值给p.RestArgs。
// 与Prog配对。GetFrom3它可以帮助模拟Prog。从3开始。
// 
// 不推荐使用：原因与Prog相同。从3开始。
func (p *Prog) SetFrom3(a Addr) {
	p.RestArgs = []AddrPos{{a, Source}}
}

// SetFrom3Reg使用包含reg的寄存器Addr调用p.SetFrom3。
// 
// 已弃用：原因与Prog相同。从3开始。
func (p *Prog) SetFrom3Reg(reg int16) {
	p.SetFrom3(Addr{Type: TYPE_REG, Reg: reg})
}

// SetFrom3Const使用包含x的常量Addr调用p.SetFrom3。
// 
// 已弃用：原因与Prog相同。从3开始。
func (p *Prog) SetFrom3Const(off int64) {
	p.SetFrom3(Addr{Type: TYPE_CONST, Offset: off})
}

// 当第二个目标
// 操作数不适合prog时，SetTo2将[]Args{{a，1}赋值给p.RestArgs。RegTo2。
func (p *Prog) SetTo2(a Addr) {
	p.RestArgs = []AddrPos{{a, Destination}}
}

// GetTo2返回第二个目标操作数。
func (p *Prog) GetTo2() *Addr {
	if p.RestArgs == nil {
		return nil
	}
	return &p.RestArgs[0].Addr
}

// SetRestArgs将多个源操作数分配给p.RestArgs。
func (p *Prog) SetRestArgs(args []Addr) {
	for i := range args {
		p.RestArgs = append(p.RestArgs, AddrPos{args[i], Source})
	}
}

// As表示汇编操作码。
// 有一些可移植的操作码，在包obj、
// 中声明，它们是所有体系结构所共有的。
// 然而，大多数操作码都是特定于arch的
// 并在各自体系结构的子包中声明。
type As int16

// 这些是便携式操作码。
const (
	AXXX As = iota
	ACALL
	ADUFFCOPY
	ADUFFZERO
	AEND
	AFUNCDATA
	AJMP
	ANOP
	APCALIGN
	APCDATA
	ARET
	AGETCALLERPC
	ATEXT
	AUNDEF
	A_ARCHSPECIFIC
)

// 每个体系结构分配了一个不同的操作码值子空间
// 用于声明其特定于arch的操作码。
// 在这个子空间中，第一个特定于arch的操作码应该是
// 偏移量A_ARCHSPECIFIC。
// 
// 子空间以二次方对齐，因此操作码可以用AMask屏蔽
// 并用作紧凑阵列索引。
const (
	ABase386 = (1 + iota) << 11
	ABaseARM
	ABaseAMD64
	ABasePPC64
	ABaseARM64
	ABaseMIPS
	ABaseRISCV
	ABaseS390X
	ABaseWasm

	AllowedOpCodes = 1 << 11            // 任何给定体系结构可用的操作码数量。
	AMask          = AllowedOpCodes - 1 // 然后使用操作码作为数组索引。
)

// LSym是写入对象文件的一种符号。
// 它代表扁平包装中的围棋符号+”+名称空间。
type LSym struct {
	Name string
	Type objabi.SymKind
	Attribute

	Size   int64
	Gotype *LSym
	P      []byte
	R      []Reloc

	Extra *interface{} // /*FuncInfo或*FileInfo（如果存在）

	Pkg    string
	PkgIdx int32
	SymIdx int32
}

// FuncInfo包含用于STEXT符号的额外字段。
type FuncInfo struct {
	Args     int32
	Locals   int32
	Align    int32
	FuncID   objabi.FuncID
	FuncFlag objabi.FuncFlag
	Text     *Prog
	Autot    map[*LSym]struct{}
	Pcln     Pcln
	InlMarks []InlMark
	spills   []RegSpill

	dwarfInfoSym       *LSym
	dwarfLocSym        *LSym
	dwarfRangesSym     *LSym
	dwarfAbsFnSym      *LSym
	dwarfDebugLinesSym *LSym

	GCArgs             *LSym
	GCLocals           *LSym
	StackObjects       *LSym
	OpenCodedDeferInfo *LSym
	ArgInfo            *LSym // 用于回溯的参数信息
	ArgLiveInfo        *LSym // 用于回溯的参数活性信息
	WrapInfo           *LSym // 对于包装器，包装函数的信息

	FuncInfoSym *LSym
}

// NewFuncInfo为LSym分配并返回FuncInfo。
func (s *LSym) NewFuncInfo() *FuncInfo {
	if s.Extra != nil {
		panic(fmt.Sprintf("invalid use of LSym - NewFuncInfo with Extra of type %T", *s.Extra))
	}
	f := new(FuncInfo)
	s.Extra = new(interface{})
	*s.Extra = f
	return f
}

// Func返回与s关联的*FuncInfo，否则返回nil。
func (s *LSym) Func() *FuncInfo {
	if s.Extra == nil {
		return nil
	}
	f, _ := (*s.Extra).(*FuncInfo)
	return f
}

// 文件信息包含文件支持的SDATA符号的额外字段。
// /（如果LSym.Extra是*FileInfo，那么LSym.P==nil。）
type FileInfo struct {
	Name string // 要读入目标文件的文件名
	Size int64  // 文件长度
}

// NewFileInfo为LSym分配并返回一个FileInfo。
func (s *LSym) NewFileInfo() *FileInfo {
	if s.Extra != nil {
		panic(fmt.Sprintf("invalid use of LSym - NewFileInfo with Extra of type %T", *s.Extra))
	}
	f := new(FileInfo)
	s.Extra = new(interface{})
	*s.Extra = f
	return f
}

// File返回与s关联的*FileInfo，否则返回nil。
func (s *LSym) File() *FileInfo {
	if s.Extra == nil {
		return nil
	}
	f, _ := (*s.Extra).(*FileInfo)
	return f
}

type InlMark struct {
	// 当从内联正文中的指令中退出时，请标记
	// 我们应该在哪里退出。
	// id记录内联体的全局内联id。
	// p记录一条指令在父（内联）帧中的位置。
	p  *Prog
	id int32
}

// 当
// “展开”内联全局帧id时，将p标记为设置为pc的指令。通常它应该是
// 指令，在调用站点上有一个file:行，并且出现在
// 内联函数体之前。
func (fi *FuncInfo) AddInlMark(p *Prog, id int32) {
	fi.InlMarks = append(fi.InlMarks, InlMark{p: p, id: id})
}

// AddSpill将溢出记录附加到FuncInfo fi 
func (fi *FuncInfo) AddSpill(s RegSpill) {
	fi.spills = append(fi.spills, s)
}

// 记录自动变量的类型符号，以便链接器
// 发出该类型的矮人类型信息。
func (fi *FuncInfo) RecordAutoType(gotype *LSym) {
	if fi.Autot == nil {
		fi.Autot = make(map[*LSym]struct{})
	}
	fi.Autot[gotype] = struct{}{}
}

// go:generate stringer-type ABI 

// ABI是文本符号的调用约定。
type ABI uint8

const (
	// ABI0是基于堆栈的稳定ABI。重要的是，它的
	// 值为“0”：我们无法区分
	// 对数据的引用和汇编代码中的ABI0文本符号，
	// 因此这不能区分没有
	// 的符号和带有ABI0的文本符号。
	ABI0 ABI = iota

	// ABI internal是在Go 
	// 版本之间可能发生变化的内部ABI。所有Go函数都使用内部ABI，
	// 编译器为其他
	// ABI的调用生成包装器。
	ABIInternal

	ABICount
)

// ParseABI将“abistr”中的字符串表示形式转换为
// 对应的ABI值。如果
// abi字符串被识别，则第二个返回值为TRUE，否则为FALSE。
func ParseABI(abistr string) (ABI, bool) {
	switch abistr {
	default:
		return ABI0, false
	case "ABI0":
		return ABI0, true
	case "ABIInternal":
		return ABIInternal, true
	}
}

// ABISet是一组ABI值。
type ABISet uint8

const (
	// ABISetCallable是所有ABI的集合，任何函数都可以使用
	// 进行调用。
	ABISetCallable ABISet = (1 << ABI0) | (1 << ABIInternal)
)

// 确保ABISet足够大，可以容纳所有ABI。
var _ ABISet = 1 << (ABICount - 1)

func ABISetOf(abi ABI) ABISet {
	return 1 << abi
}

func (a *ABISet) Set(abi ABI, value bool) {
	if value {
		*a |= 1 << abi
	} else {
		*a &^= 1 << abi
	}
}

func (a *ABISet) Get(abi ABI) bool {
	return (*a>>abi)&1 != 0
}

func (a ABISet) String() string {
	s := "{"
	for i := ABI(0); a != 0; i++ {
		if a&(1<<i) != 0 {
			if s != "{" {
				s += ","
			}
			s += i.String()
			a &^= 1 << i
		}
	}
	return s + "}"
}

// 属性是一组符号属性。
type Attribute uint32

const (
	AttrDuplicateOK Attribute = 1 << iota
	AttrCFunc
	AttrNoSplit
	AttrLeaf
	AttrWrapper
	AttrNeedCtxt
	AttrNoFrame
	AttrOnList
	AttrStatic

	// MakeTypelink表示该类型在typelink表中应该有一个条目。
	AttrMakeTypelink

	// ReflectMethod表示函数可以调用reflect。类型方法或
	// 反射。类型MethodByName。匹配不精确（因为reflect.Type 
	// 可以通过自定义接口使用），所以ReflectMethod在某些情况下可能是
	// 在未调用reflect包时设置的。
	// 
	// 链接器用于确定可以删除哪些方法。
	AttrReflectMethod

	// 本地意味着即使在编译Go代码以引用Go 
	// 其他共享库中的符号时，也要将符号设置为本地，因为在这种模式下，符号是全局的，默认值为
	// 默认值。“本地”在这里是指动态链接器，即在包含其
	// 本地。（在不编译以支持Go共享库时，所有符号在这个意义上都是
	// 定义的模块（共享库或可执行文件）之外不可见的
	// local，除非有cgo_export_*指令）。
	AttrLocal

	// 用于功能符号；指示指定的函数是编译期间内联的
	// 目标
	AttrWasInlined

	// 索引表示此符号已分配索引（使用
	// 新对象文件格式时）。
	AttrIndexed

	// 仅适用于类型描述符符号，UsedInFace表示此类型已转换为接口。
	// 
	// 链接器用于确定可以删除哪些方法。
	AttrUsedInIface

	// ContentAddressable表示这是一个内容寻址符号。
	AttrContentAddressable

	// ABI包装器是为编译器生成的文本符号设置的，
	// 在ABI0和ABI内部调用约定之间转换。
	AttrABIWrapper

	// IsPcdata表示这是一个pcdata符号。
	AttrPcdata

	// attrABIBase是ABI在
	// 属性中编码的值。这必须是最后一次；在此之后的所有位都是
	// 假定为ABI值。
	// 
	// 必须是最后一位，因为上面的所有位都构成ABI。
	attrABIBase
)

func (a *Attribute) load() Attribute { return Attribute(atomic.LoadUint32((*uint32)(a))) }

func (a *Attribute) DuplicateOK() bool        { return a.load()&AttrDuplicateOK != 0 }
func (a *Attribute) MakeTypelink() bool       { return a.load()&AttrMakeTypelink != 0 }
func (a *Attribute) CFunc() bool              { return a.load()&AttrCFunc != 0 }
func (a *Attribute) NoSplit() bool            { return a.load()&AttrNoSplit != 0 }
func (a *Attribute) Leaf() bool               { return a.load()&AttrLeaf != 0 }
func (a *Attribute) OnList() bool             { return a.load()&AttrOnList != 0 }
func (a *Attribute) ReflectMethod() bool      { return a.load()&AttrReflectMethod != 0 }
func (a *Attribute) Local() bool              { return a.load()&AttrLocal != 0 }
func (a *Attribute) Wrapper() bool            { return a.load()&AttrWrapper != 0 }
func (a *Attribute) NeedCtxt() bool           { return a.load()&AttrNeedCtxt != 0 }
func (a *Attribute) NoFrame() bool            { return a.load()&AttrNoFrame != 0 }
func (a *Attribute) Static() bool             { return a.load()&AttrStatic != 0 }
func (a *Attribute) WasInlined() bool         { return a.load()&AttrWasInlined != 0 }
func (a *Attribute) Indexed() bool            { return a.load()&AttrIndexed != 0 }
func (a *Attribute) UsedInIface() bool        { return a.load()&AttrUsedInIface != 0 }
func (a *Attribute) ContentAddressable() bool { return a.load()&AttrContentAddressable != 0 }
func (a *Attribute) ABIWrapper() bool         { return a.load()&AttrABIWrapper != 0 }
func (a *Attribute) IsPcdata() bool           { return a.load()&AttrPcdata != 0 }

func (a *Attribute) Set(flag Attribute, value bool) {
	for {
		v0 := a.load()
		v := v0
		if value {
			v |= flag
		} else {
			v &^= flag
		}
		if atomic.CompareAndSwapUint32((*uint32)(a), uint32(v0), uint32(v)) {
			break
		}
	}
}

func (a *Attribute) ABI() ABI { return ABI(a.load() / attrABIBase) }
func (a *Attribute) SetABI(abi ABI) {
	const mask = 1 // 目前只有一个ABI位。
	for {
		v0 := a.load()
		v := (v0 &^ (mask * attrABIBase)) | Attribute(abi)*attrABIBase
		if atomic.CompareAndSwapUint32((*uint32)(a), uint32(v0), uint32(v)) {
			break
		}
	}
}

var textAttrStrings = [...]struct {
	bit Attribute
	s   string
}{
	{bit: AttrDuplicateOK, s: "DUPOK"},
	{bit: AttrMakeTypelink, s: ""},
	{bit: AttrCFunc, s: "CFUNC"},
	{bit: AttrNoSplit, s: "NOSPLIT"},
	{bit: AttrLeaf, s: "LEAF"},
	{bit: AttrOnList, s: ""},
	{bit: AttrReflectMethod, s: "REFLECTMETHOD"},
	{bit: AttrLocal, s: "LOCAL"},
	{bit: AttrWrapper, s: "WRAPPER"},
	{bit: AttrNeedCtxt, s: "NEEDCTXT"},
	{bit: AttrNoFrame, s: "NOFRAME"},
	{bit: AttrStatic, s: "STATIC"},
	{bit: AttrWasInlined, s: ""},
	{bit: AttrIndexed, s: ""},
	{bit: AttrContentAddressable, s: ""},
	{bit: AttrABIWrapper, s: "ABIWRAPPER"},
}

// 字符串格式a，用于作为文本程序的一部分进行打印。
func (a Attribute) String() string {
	var s string
	for _, x := range textAttrStrings {
		if a&x.bit != 0 {
			if x.s != "" {
				s += x.s + "|"
			}
			a &^= x.bit
		}
	}
	switch a.ABI() {
	case ABI0:
	case ABIInternal:
		s += "ABIInternal|"
		a.SetABI(0) // 清除ABI，这样我们就不会在下面打印了。
	}
	if a != 0 {
		s += fmt.Sprintf("UnknownAttribute(%d)|", a)
	}
	// 如果存在尾随，则切掉尾随。
	if len(s) > 0 {
		s = s[:len(s)-1]
	}
	return s
}

// TEXTATRTSTRING将用于打印的符号属性格式化为文本程序的一部分。
func (s *LSym) TextAttrString() string {
	attr := s.Attribute.String()
	if s.Func().FuncFlag&objabi.FuncFlag_TOPFRAME != 0 {
		if attr != "" {
			attr += "|"
		}
		attr += "TOPFRAME"
	}
	return attr
}

func (s *LSym) String() string {
	return s.Name
}

// 编译器需要*LSym才能分配给cmd/compile/internal/ssa。赛姆。
func (*LSym) CanBeAnSSASym() {}
func (*LSym) CanBeAnSSAAux() {}

type Pcln struct {
	// pcln的辅助符号
	Pcsp      *LSym
	Pcfile    *LSym
	Pcline    *LSym
	Pcinline  *LSym
	Pcdata    []*LSym
	Funcdata  []*LSym
	UsedFiles map[goobj.CUFileIndex]struct{} // 生成pcfile时使用的文件索引
	InlTree   InlTree                        // 从全局树提取的每个函数内联树
}

type Reloc struct {
	Off  int32
	Siz  uint8
	Type objabi.RelocType
	Add  int64
	Sym  *LSym
}

type Auto struct {
	Asym    *LSym
	Aoffset int32
	Name    AddrName
	Gotype  *LSym
}

// RegSpill为函数提供寄存器驻留参数
// 的溢出/填充信息。这些需要在safepoint/stackgrowth案例中溢出/填充。
// 填充/溢出时，必须通过依赖于体系结构的
// 调整调用指令中出现的硬件SP来调整偏移量。例如，对于AMD64，
// 位于偏移量+8处，因为返回地址被推送。
type RegSpill struct {
	Addr           Addr
	Reg            int16
	Spill, Unspill As
}

// Link保存从编译器写入目标代码的上下文
// 作为链接器输入或将该输入读取到链接器中。
type Link struct {
	Headtype           objabi.HeadType
	Arch               *LinkArch
	Debugasm           int
	Debugvlog          bool
	Debugpcln          string
	Flag_shared        bool
	Flag_dynlink       bool
	Flag_linkshared    bool
	Flag_optimize      bool
	Flag_locationlists bool
	Retpoline          bool   // emit use of retpoline stubs for indirect jmp/call 
	Flag_maymorestack  string // /如果不是“”，请在堆栈检查前调用此函数
	Bso                *bufio.Writer
	Pathname           string
	Pkgpath            string           // 当前包的导入路径“”，如果未知
	hashmu             sync.Mutex       // 保护哈希，funchash 
	hash               map[string]*LSym // name->sym mapping 
	funchash           map[string]*LSym // name->sym mapping for ABIInternal syms 
	statichash         map[string]*LSym // name->sym mapping for static syms 
	PosTable           src.PosTable
	InlTree            InlTree // gc inl使用的全局内联树。go 
	DwFixups           *DwarfFixupTable
	Imports            []goobj.ImportedPkg
	DiagFunc           func(string, ...interface{})
	DiagFlush          func()
	DebugInfo          func(fn *LSym, info *LSym, curfn interface{}) ([]dwarf.Scope, dwarf.InlCalls) // 如果非零，curfn是一个*gc。节点
	GenAbstractFunc    func(fn *LSym)
	Errors             int

	InParallel    bool // 并行后端阶段生效
	UseBASEntries bool // 在位置列表和PC范围中使用基址选择项
	IsAsm         bool // 是源汇编语言，其中可能包含令人惊讶的习语（例如，调用表）

	// 用于写入对象的状态
	Text []*LSym
	Data []*LSym

	// 常量符号（例如$i64.*）是在并发阶段后期创建的数据符号
	// 。为了确保确定的顺序，我们将它们添加到一个单独的列表中，在末尾排序，并将其附加到数据中。
	constSyms []*LSym

	// pkgIdx将包路径映射到索引。该索引用于对象文件中的
	// 符号引用。
	pkgIdx map[string]int32

	defs         []*LSym // 当前包中已定义符号列表
	hashed64defs []*LSym // 已定义短（64位或更少）散列（可内容寻址）符号列表
	hasheddefs   []*LSym // 已定义散列（可内容寻址）符号列表
	nonpkgdefs   []*LSym // 已定义非包符号列表
	nonpkgrefs   []*LSym // 参考非包符号列表

	Fingerprint goobj.FingerprintType // 指纹符号索引，用于捕捉索引不匹配的
}

func (ctxt *Link) Diag(format string, args ...interface{}) {
	ctxt.Errors++
	ctxt.DiagFunc(format, args...)
}

func (ctxt *Link) Logf(format string, args ...interface{}) {
	fmt.Fprintf(ctxt.Bso, format, args...)
	ctxt.Bso.Flush()
}

// 溢出寄存器args发出代码，将寄存器args溢出到溢出记录指定的任何
// 位置。
func (fi *FuncInfo) SpillRegisterArgs(last *Prog, pa ProgAlloc) *Prog {
	// 溢出寄存器参数。
	for _, ra := range fi.spills {
		spill := Appendp(last, pa)
		spill.As = ra.Spill
		spill.From.Type = TYPE_REG
		spill.From.Reg = ra.Reg
		spill.To = ra.Addr
		last = spill
	}
	return last
}

// UnspillRegisterArgs发出代码，从溢出记录指定的任何
// 位置恢复寄存器args。
func (fi *FuncInfo) UnspillRegisterArgs(last *Prog, pa ProgAlloc) *Prog {
	// 取消填充任何溢出的寄存器参数
	for _, ra := range fi.spills {
		unspill := Appendp(last, pa)
		unspill.As = ra.Unspill
		unspill.From = ra.Addr
		unspill.To.Type = TYPE_REG
		unspill.To.Reg = ra.Reg
		last = unspill
	}
	return last
}

// 从硬件堆栈指针到堆栈上本地
// 变量的最小可能偏移量。使用链接寄存器的架构将其值
// 保存在函数序言中的堆栈上，因此始终在
// 硬件堆栈指针和局部变量区域之间有一个指针。
func (ctxt *Link) FixedFrameSize() int64 {
	switch ctxt.Arch.Family {
	case sys.AMD64, sys.I386, sys.Wasm:
		return 0
	case sys.PPC64:
		// ppc64le上的PIC代码需要32字节的堆栈，而
		// 只需在ppc64x上始终使用那么多堆栈就可以了。
		return int64(4 * ctxt.Arch.PtrSize)
	default:
		return int64(ctxt.Arch.PtrSize)
	}
}

// LinkArch是单个架构的定义。
type LinkArch struct {
	*sys.Arch
	Init           func(*Link)
	ErrorCheck     func(*Link, *LSym)
	Preprocess     func(*Link, *LSym, ProgAlloc)
	Assemble       func(*Link, *LSym, ProgAlloc)
	Progedit       func(*Link, *Prog, ProgAlloc)
	UnaryDst       map[As]bool // 指令接受一个操作数，即目标。
	DWARFRegisters map[int16]int16
}
